package com.recharge.plugins;

import com.alibaba.fastjson.JSON;
import com.recharge.InvocationHandler.SqlSourceInterceptor;
import com.recharge.common.util.Constant;
import com.recharge.common.util.RedisUtil;
import com.recharge.domain.context.MyRedisSerializer;
import com.recharge.domain.vo.RoleDO;
import com.recharge.domain.vo.UserLoginDO;
import com.recharge.shiro.MySerializeUtil;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.JdbcParameter;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.parser.SimpleNode;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.Statements;
import net.sf.jsqlparser.statement.select.*;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.shiro.SecurityUtils;

import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;


import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.*;
import java.util.logging.Logger;

@Component
@Intercepts(@Signature(type= Executor.class,method = "query",args={MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class}))
public class PermissionPlugins implements Interceptor {

    private static RedisUtil redisUtil;



//    private static RedisUtil redisSessionDAO = SpringUtil.getBean(RedisUtil.class);

    private Logger logger = Logger.getLogger(this.getClass().getName());

    private DefaultWebSecurityManager manager=null;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if(manager==null){
            manager = new DefaultWebSecurityManager();
            ThreadContext.bind(manager);
        }
        if(redisUtil==null){
            RedisTemplate<String,Object> temp = new RedisTemplate<>();
            RedisSerializer stringSerializer = new StringRedisSerializer();
            temp.setKeySerializer(stringSerializer);
            temp.setValueSerializer(new MySerializeUtil());
            temp.setHashKeySerializer(stringSerializer);
            temp.setHashValueSerializer(new MySerializeUtil());
            JedisConnectionFactory fac = new JedisConnectionFactory(new JedisPoolConfig());
            fac.setDatabase(0);
            JedisShardInfo shardInfo = new JedisShardInfo("127.0.0.1", 6379);
            fac.setShardInfo(shardInfo);
            temp.setConnectionFactory(fac);
            temp.afterPropertiesSet();
            redisUtil = new RedisUtil(temp);
        }
        final Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement)args[0];
        SqlSource sqlSource = ms.getSqlSource();
        SqlSource w = SqlSourceInterceptor.wrap(sqlSource, SqlSource.class);
        BoundSql boundSql = w.getBoundSql(args[1]);
        if(boundSql.getSql().equals("")){
            return new ArrayList<>();
        }
        Field field = ms.getClass().getDeclaredField("sqlSource");
        field.setAccessible(true);
        field.set(ms, w);
        return invocation.proceed();
    }

    public static BoundSql modifyBoundSql(BoundSql boundSql) throws Exception {
        //获取初始sql
        String originSql = boundSql.getSql();
        //修改sql
        String changeSql = parseSql(originSql);
        Field field = boundSql.getClass().getDeclaredField("sql");
        field.setAccessible(true);
        field.set(boundSql, changeSql);
        return boundSql;


    }

    private static String parseSql(String sql) throws Exception {

        StringBuffer newSql = new StringBuffer();
        try {
            //解析sql 获得结构化statement
            Statements s = CCJSqlParserUtil.parseStatements(sql);
            //对每一个statement
            for(Statement st : s.getStatements()) {
                if(st!=null) {
                    //查询sql处理
                    if(st instanceof Select) {
                        SelectBody selectBody = ((Select) st).getSelectBody();
                        Map<String,List<String>> blockList = getBlockCols();
                        String tablename=null;
                        String asname=null;
                        String jointablename=null;
                        String joinasname=null;
                        if(selectBody instanceof PlainSelect) {
                            //进行行数据权限
                            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
                            UserLoginDO user = null;
                            if(servletRequestAttributes!=null){
                                Object userstr  = redisUtil.get(Constant.USER+servletRequestAttributes.getRequest().getHeader("authToken"));
                                if(userstr!=null&&userstr instanceof UserLoginDO){
                                    user=(UserLoginDO) redisUtil.get(Constant.USER+servletRequestAttributes.getRequest().getHeader("authToken"));
                                    if(servletRequestAttributes.getRequest().getHeader("authToken")!=null){
                                        if(user!=null){
                                            if(((Table) ((PlainSelect)selectBody).getFromItem())!=null&&((Table) ((PlainSelect)selectBody).getFromItem()).getName()!=null){
                                                //获取表名和别名
                                                tablename = ((Table) ((PlainSelect)selectBody).getFromItem()).getName().replaceAll("`","");
                                                asname = ((Table) ((PlainSelect)selectBody).getFromItem()).getAlias()==null?null:((Table) ((PlainSelect)selectBody).getFromItem()).getAlias().getName();
                                                //权限处理部分  确定表名获取where部分sql
                                                Expression expression = ((PlainSelect) selectBody).getWhere();
                                                EqualsTo eqto = new EqualsTo();
                                                Column column = new Column();
                                                StringValue merchantId = new StringValue(user.getId());
                                                if(tablename.equals("sys_merchant")){
                                                    if (asname!=null){
                                                        column.setColumnName(asname+".id");
                                                    }else{
                                                        column.setColumnName("id");
                                                    }
                                                }else if(tablename.equals("business_order")||tablename.equals("business_provider_order")||tablename.equals("report_order_goods_statis")||tablename.equals("report_provider_statis")){
                                                    if (asname!=null){
                                                        column.setColumnName(asname+".merchant_id");
                                                    }else{
                                                        column.setColumnName("merchant_id");
                                                    }
                                                }
                                                eqto.setLeftExpression(column);
                                                eqto.setRightExpression(merchantId);
                                                if(expression==null){
                                                    ((PlainSelect) selectBody).setWhere(eqto);
                                                }else{
                                                    AndExpression and = new AndExpression(expression,eqto);
                                                    ((PlainSelect) selectBody).setWhere(and);
                                                }
                                            }
                                            if(((PlainSelect)selectBody).getJoins()!=null){
                                                for(Join j :((PlainSelect)selectBody).getJoins()){
                                                    jointablename = ((Table)j.getRightItem()).getName();
                                                    joinasname = ((Table)j.getRightItem()).getAlias()==null?null:((Table)j.getRightItem()).getAlias().getName();
                                                    Expression expression = ((PlainSelect) selectBody).getWhere();
                                                    EqualsTo eqto = new EqualsTo();
                                                    Column column = new Column();
                                                    StringValue merchantId = new StringValue(user.getMerchantId());
                                                    if(jointablename.equals("sys_merchant")){
                                                        if (joinasname!=null){
                                                            column.setColumnName(joinasname+".id");
                                                        }else{
                                                            column.setColumnName("id");
                                                        }
                                                    }else if(jointablename.equals("business_order")||jointablename.equals("business_provider_order")|| jointablename.equals("report_order_goods_statis")||jointablename.equals("report_provider_statis")){
                                                        if (joinasname!=null){
                                                            column.setColumnName(joinasname+".merchant_id");
                                                        }else{
                                                            column.setColumnName("merchant_id");
                                                        }
                                                    }
                                                    eqto.setLeftExpression(column);
                                                    eqto.setRightExpression(merchantId);
                                                    if(expression==null){
                                                        ((PlainSelect) selectBody).setWhere(eqto);
                                                    }else{
                                                        AndExpression and = new AndExpression(expression,eqto);
                                                        ((PlainSelect) selectBody).setWhere(and);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }


                            //进行列数据权限筛选
                            Iterator<SelectItem> it = ((PlainSelect) selectBody).getSelectItems().iterator();
                            //遍历查询字段
                            while(it.hasNext()) {
                                SelectItem si = it.next();
                                if(si instanceof SelectExpressionItem) {
                                    if(blockList!=null){
                                        if(tablename!=null){
                                            if(blockList.get(tablename)!=null){
                                                boolean bo = true;
                                                //从map内取出对应表的白名单循环判断 如果存在就不删除 循环完毕仍然为true则删除该列
                                                if(blockList.get(tablename)!=null){
                                                    for(String str : blockList.get(tablename)) {
                                                        if(si.toString().contains(str)) {
                                                            bo=false;
                                                            break;
                                                        }
                                                    }
                                                }
                                                if(bo){
                                                    it.remove();
                                                }
                                            }
                                        }
                                        if(((PlainSelect)selectBody).getJoins()!=null){
                                            //取join部分表名
                                            //从map内取出对应表的白名单循环判断 如果存在就不删除 循环完毕仍然为true则删除该列
                                            for(Join j :((PlainSelect)selectBody).getJoins()){
                                                boolean bo = true;
                                                if(blockList!=null&&blockList.size()>0){
                                                    if(j.getRightItem() instanceof Table){
                                                        if(blockList.get(((Table)j.getRightItem()).getName())!=null){
                                                            for(String str : blockList.get(((Table)j.getRightItem()).getName())) {
                                                                if(si.toString().contains(str)) {
                                                                    bo=false;
                                                                    break;
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                                if(bo){
                                                    it.remove();
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        newSql.append(st);
                    }
                    if(((PlainSelect) ((Select) st).getSelectBody()).getSelectItems().size()==0){
                        newSql.setLength(0);
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return newSql.toString();
    }



    private static  Map<String,List<String>> getBlockCols(){
//        System.out.println(request.getHeader("authToken"));
     ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
       if(servletRequestAttributes!=null){
           if(servletRequestAttributes.getRequest().getHeader("authToken")!=null){
               List<String> list = new ArrayList<>();
               Object userstr = redisUtil.get(Constant.USER+servletRequestAttributes.getRequest().getHeader("authToken"));
                if(userstr!=null&&(userstr instanceof UserLoginDO)){
                    UserLoginDO user = (UserLoginDO) userstr;
                    Map<String,List<String>> map = null;
                    if(redisUtil.get(Constant.ROLE_DATA_PERMISSION+user.getId())!=null&&!redisUtil.get(Constant.ROLE_DATA_PERMISSION+user.getId()).equals("")){
                        String str =(String)redisUtil.get(Constant.ROLE_DATA_PERMISSION+user.getId());
                        str = StringEscapeUtils.unescapeEcmaScript(str);
                        map = JSON.parseObject(str,Map.class);
                    }
                    return map;
                }
           }
       }
       return null;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target,this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
